The fish belt transect (BLT) protocol is the primary method used to quantify reef fish assemblages at UVS sites. Divers swim standardized belt transects, visually identifying and sizing all fishes observed within a defined area. This method yields robust estimates of fish abundance, biomass, and community structure across depth strata and regions (see SOP for full methods).
Fish BLT data are stored into three tables:
uvs.blt_stations — Metadata for each station, including depth, diver, effort, and summary metrics
uvs.blt_observations — Raw fish counts by species, size class, and transect
uvs.blt_biomass_by_taxa — Derived biomass by station and taxon, using length-weight relationships
All records are linked via ps_station_id, and species names are standardized using taxon_id from taxonomy.fish.
uvs.blt_stations
This table contains one row per depth-stratified fish survey conducted at a UVS site. Each row includes dive metadata, survey effort (e.g., number of transects, area surveyed), and ecological summaries such as richness, total abundance, and biomass. Stations are linked to uvs.sites via ps_site_id. The ps_station_id is generated by appending a standardized depth bin label to the ps_site_id.
Standard depth strata for UVS methods
To ensure consistency across sites and protocols, station depth (depth_m) is binned into standard strata using fixed thresholds. The binned depth label is then appended to the ps_site_id to generate a unique ps_station_id.
Depth strata and labels:
Supershallow: <= 6 m → 05m
Shallow: 7–14 m → 10m
Deep: >= 15 m → 20m
Examples:
If a fish transect is conducted at 5.8 m → station ID = FJI_2025_uvs_001_05m
If the same site has another transect at 14.7 m → FJI_2025_uvs_001_10m
This depth binning scheme ensures consistent stratification across analyses, regardless of slight depth variation between dives.
Table 1: Fields in the uvs.blt_stations table
Field
Type
Required
Description
ps_station_id
STRING
true
Unique station ID (ps_site_id_depth), e.g., CHL_2024_uvs_001_20m
ps_site_id
STRING
true
◂ Foreign key to UVS site table
exp_id
STRING
true
◂ Expedition ID (ISO3_YEAR)
method
STRING
true
◂ Survey method (uvs)
protocol
STRING
true
Survey protocol (fish_blt) used at the station
divers
STRING
true
Names of divers who conducted the transects (comma-separated)
date
DATE
true
◂ Survey date (YYYY-MM-DD)
time
TIME
true
◂ Survey start time (24-hour format)
depth_m
FLOAT
true
Average depth of the station (m)
depth_strata
STRING
true
Binned depth category: supershallow, shallow, or deep
region
STRING
true
◂ Region name
subregion
STRING
true
◂ Subregion name
locality
STRING
false
◂ Optional local feature (e.g., reef, bay, cove)
n_transects
INTEGER
true
Number of fish transects completed at the station
transect_length
FLOAT
true
Length of each transect (m)
transect_width
FLOAT
true
Width of each transect (m)
survey_area_m2
FLOAT
true
Total area surveyed at the station (m²)
habitat
STRING
true
◂ Habitat type (see controlled vocabulary)
exposure
STRING
true
◂ Exposure type (see controlled vocabulary)
n_species
INTEGER
false
Species richness (optional summary)
abundance_m2
FLOAT
false
Fish abundance (individuals/m², optional summary)
biomass_gm2
FLOAT
false
Fish biomass (grams/m², optional summary)
notes
STRING
false
Optional comments or QA notes
uvs.blt_observations
This table contains one row per fish observation recorded on an individual transect during a belt transect survey. Observations represent raw field data collected by divers and include species identity, estimated size, and count. Each row corresponds to a unique combination of species, size class, and transect.
Linked to the uvs.blt_stations table via ps_station_id
Taxonomic identity is standardized using accepted_name and aphia_id, which join to taxonomy.fish
Fish size (length_cm) and abundance (count) are used to calculate derived metrics (e.g., biomass) in downstream summaries
Transect dimensions by fish size
Fish ≥ 20 cm are recorded along a 25 m × 4 m belt (100 m²)
Fish < 20 cmare recorded along a 25 m × 2 m belt (50 m²) Note: When transects deviate from standard dimensions, actual surveyed area should be recorded and used for calculations.
Table 2: fish.observations Table Schema
Field
Type
Required
Description
obs_id
STRING
true
Unique observation ID from diver datasheet (e.g., CHL_2024_blt_AMF_0001)
Transect label: A, B, or C; OFF for observations recorded outside standard transects
depth_m
FLOAT
true
Depth at which the transect was conducted
accepted_name
STRING
true
Scientific name (Genus species) of observed taxon
aphia_id
STRING
true
Unique AphiaID from WoRMS — links to taxonomy.fish
length_cm
FLOAT
true
Estimated total length (cm) of individual or size class
terminal_phase
BOOLEAN
false
TRUE if individual was identified as terminal phase (e.g., parrotfishes)
count
INTEGER
true
Number of individuals observed in this size class
notes
STRING
false
Optional comments or QA notes
Off-transect observations
Fish observed outside of standard transects (e.g., large pelagics, megafauna) are recorded using transect = "OFF". These are included for completeness but should be treated separately in standardized analyses.
uvs.blt_biomass_by_taxa
This table contains station-level summaries of reef fish biomass and abundance by species, derived from raw BLT transect observations. It is the primary analysis-ready table for generating summaries, figures, and community structure plots.
Each row represents a unique combination of ps_station_id and species, with metrics standardized to g/m² (biomass) and ind./m² (abundance). All values are computed using species-specific length–weight parameters from the taxonomy.fish table.
Taxonomic and ecological fields — including family, trophic group, and functional group — are joined from the central species reference and enable flexible aggregation for trophic or functional analyses.
Table 3: uva.blt_biomass_by_taxa Table Schema
Field
Type
Required
Description
ps_station_id
STRING
true
◂ Foreign key to uvs.blt_stations
exp_id
STRING
true
◂ Expedition ID (ISO3_YEAR)
ps_site_id
STRING
true
◂ Site ID for spatial grouping
region
STRING
true
◂ Region name
subregion
STRING
true
◂ Subregion name
depth_strata
STRING
true
Binned depth category: supershallow, shallow, or deep
depth_m
FLOAT
true
Average depth of the station (m)
aphia_id
STRING
true
◂ Species ID (linked to taxonomy.fish)
accepted_name
STRING
false
Scientific name (Genus species)
family
STRING
false
Taxonomic family (e.g., Acanthuridae)
trophic_group
STRING
false
Ecological trophic group (e.g., piscivore, herbivore)
---title: "Fish Belt Transect (BLT) Surveys"format: html: theme: lux self-contained: true code-fold: true toc: true toc-depth: 3 toc-location: right number-sections: true number-depth: 3---```{r setup, message = F, warning = F, fig.width = 10, fig.height = 10, echo = F}options(scipen = 999)library(PristineSeasR)library(bigrquery)library(gt)library(gtExtras)library(tibble)library(tidyverse)library(dplyr)library(ggplot2)library(plotly)library(lubridate)knitr::opts_chunk$set(eval = F, warning = F, message = F, include = F, echo = F)ps_paths <- PristineSeasR::get_sci_drive_paths()prj_path <- file.path(ps_paths$projects, "legacy-db")ps_data_path <- ps_paths$datasetsbigrquery::bq_auth(email = "marine.data.science@ngs.org")project_id <- "pristine-seas"bq_connection <- DBI::dbConnect(bigrquery::bigquery(), project = project_id)```#### Fish BLT SurveysThe fish belt transect (BLT) protocol is the primary method used to quantify reef fish assemblages at UVS sites. Divers swim standardized belt transects, visually identifying and sizing all fishes observed within a defined area. This method yields robust estimates of fish abundance, biomass, and community structure across depth strata and regions (see SOP for full methods).Fish BLT data are stored into three tables: - `uvs.blt_stations` — Metadata for each station, including depth, diver, effort, and summary metrics - `uvs.blt_observations` — Raw fish counts by species, size class, and transect - `uvs.blt_biomass_by_taxa` — Derived biomass by station and taxon, using length-weight relationshipsAll records are linked via `ps_station_id`, and species names are standardized using `taxon_id` from `taxonomy.fish`.##### `uvs.blt_stations`This table contains one row per depth-stratified fish survey conducted at a UVS site. Each row includes dive metadata, survey effort (e.g., number of transects, area surveyed), and ecological summaries such as richness, total abundance, and biomass. Stations are linked to `uvs.sites` via `ps_site_id`. The `ps_station_id` is generated by appending a standardized depth bin label to the `ps_site_id`.::: {.callout-note title="Standard depth strata for UVS methods"}To ensure consistency across sites and protocols, **station depth (`depth_m`) is binned into standard strata** using fixed thresholds. The binned depth label is then appended to the `ps_site_id` to generate a unique `ps_station_id`.**Depth strata and labels:**- **Supershallow**: `<= 6 m` → `05m`- **Shallow**: `7–14 m` → `10m`- **Deep**: `>= 15 m` → `20m`**Examples:**- If a fish transect is conducted at 5.8 m → station ID = `FJI_2025_uvs_001_05m`- If the same site has another transect at 14.7 m → `FJI_2025_uvs_001_10m`This depth binning scheme ensures consistent stratification across analyses, regardless of slight depth variation between dives.:::```{r eval = T, include = T}#| label: tbl-uvs.blt_stations#| tbl-cap: "Fields in the `uvs.blt_stations` table"blt_stations_fields <- tribble( ~field, ~type, ~required, ~description, # Identifiers "ps_station_id", "STRING", TRUE, "Unique station ID (`ps_site_id_depth`), e.g., `CHL_2024_uvs_001_20m`", "ps_site_id", "STRING", TRUE, "◂ Foreign key to UVS site table", "exp_id", "STRING", TRUE, "◂ Expedition ID (`ISO3_YEAR`)", "method", "STRING", TRUE, "◂ Survey method (`uvs`)", "protocol", "STRING", TRUE, "Survey protocol (`fish_blt`) used at the station", "divers", "STRING", TRUE, "Names of divers who conducted the transects (comma-separated)", # Spatial and temporal info "date", "DATE", TRUE, "◂ Survey date (`YYYY-MM-DD`)", "time", "TIME", TRUE, "◂ Survey start time (24-hour format)", "depth_m", "FLOAT", TRUE, "Average depth of the station (m)", "depth_strata", "STRING", TRUE, "Binned depth category: `supershallow`, `shallow`, or `deep`", "region", "STRING", TRUE, "◂ Region name", "subregion", "STRING", TRUE, "◂ Subregion name", "locality", "STRING", FALSE, "◂ Optional local feature (e.g., reef, bay, cove)", # Survey effort "n_transects", "INTEGER", TRUE, "Number of fish transects completed at the station", "transect_length", "FLOAT", TRUE, "Length of each transect (m)", "transect_width", "FLOAT", TRUE, "Width of each transect (m)", "survey_area_m2", "FLOAT", TRUE, "Total area surveyed at the station (m²)", # Environmental descriptors "habitat", "STRING", TRUE, "◂ Habitat type (see controlled vocabulary)", "exposure", "STRING", TRUE, "◂ Exposure type (see controlled vocabulary)", # Optional ecological summaries "n_species", "INTEGER", FALSE, "Species richness (optional summary)", "abundance_m2", "FLOAT", FALSE, "Fish abundance (individuals/m², optional summary)", "biomass_gm2", "FLOAT", FALSE, "Fish biomass (grams/m², optional summary)", # Notes "notes", "STRING", FALSE, "Optional comments or QA notes")gt(blt_stations_fields) |> cols_label(field = md("**Field**"), type = md("**Type**"), required = md("**Required**"), description = md("**Description**")) |> cols_width(field ~ px(200), type ~ px(100), required ~ px(80), description ~ px(500)) |> data_color(columns = c(field), fn = scales::col_factor(palette = c("#f6f6f6"), domain = NULL) ) |> tab_options(table.font.size = px(13), table.width = pct(100)) |> fmt_tf(columns = required, tf_style = "true-false") |> fmt_markdown(columns = description) |> gt_theme_nytimes()``````{r}# Define table schemablt_stations_schema <- blt_stations_fields |>mutate(mode =if_else(required, "REQUIRED", "NULLABLE")) |>transmute(name = field,type = type,mode = mode,description = description) |>pmap(function(name, type, mode, description) {list(name = name, type = type, mode = mode, description = description) })bq_table_create(bq_table(project_id, "uvs", "blt_stations"),fields = blt_stations_schema)```##### `uvs.blt_observations`This table contains one row per fish observation recorded on an individual transect during a belt transect survey. Observations represent raw field data collected by divers and include species identity, estimated size, and count. Each row corresponds to a unique combination of species, size class, and transect.- Linked to the uvs.blt_stations table via ps_station_id- Taxonomic identity is standardized using `accepted_name` and `aphia_id`, which join to `taxonomy.fish`- Fish size (length_cm) and abundance (count) are used to calculate derived metrics (e.g., biomass) in downstream summaries::: {.callout-note title="Transect dimensions by fish size"}- Fish **≥ 20 cm** are recorded along a 25 m × 4 m belt (100 m²)- Fish **< 20 cm**are recorded along a 25 m × 2 m belt (50 m²)*Note: When transects deviate from standard dimensions, actual surveyed area should be recorded and used for calculations.*:::```{r eval = T, include = T}#| label: tbl-fish-obs-schema#| tbl-cap: "`fish.observations` Table Schema"blt_obs_fields <- tribble( ~field, ~type, ~required, ~description, "obs_id", "STRING", TRUE, "Unique observation ID from diver datasheet (e.g., `CHL_2024_blt_AMF_0001`)", "ps_station_id", "STRING", TRUE, "◂ Foreign key linking to `uvs.blt_stations`", "diver", "STRING", TRUE, "Name of the diver who recorded the observation", "station_label", "STRING", FALSE, "Field-assigned depth stratum label (e.g., `shallow`, `deep`)", "transect", "STRING", TRUE, "Transect label: `A`, `B`, or `C`; `OFF` for observations recorded outside standard transects", "depth_m", "FLOAT", TRUE, "Depth at which the transect was conducted", "accepted_name", "STRING", TRUE, "Scientific name (`Genus species`) of observed taxon", "aphia_id", "STRING", TRUE, "Unique AphiaID from WoRMS — links to `taxonomy.fish`", "length_cm", "FLOAT", TRUE, "Estimated total length (cm) of individual or size class", "terminal_phase", "BOOLEAN", FALSE, "TRUE if individual was identified as terminal phase (e.g., parrotfishes)", "count", "INTEGER", TRUE, "Number of individuals observed in this size class", "notes", "STRING", FALSE, "Optional comments or QA notes")gt(blt_obs_fields) |> cols_label(field = md("**Field**"), type = md("**Type**"), required = md("**Required**"), description = md("**Description**")) |> cols_width(field ~ px(200), type ~ px(100), required ~ px(80), description ~ px(500)) |> data_color(columns = c(field), fn = scales::col_factor(palette = c("#f6f6f6"), domain = NULL) ) |> tab_options(table.font.size = px(13), table.width = pct(100)) |> fmt_tf(columns = required, tf_style = "true-false") |> fmt_markdown(columns = description) |> gt_theme_nytimes()```::: {.callout-note title="Off-transect observations"}Fish observed outside of standard transects (e.g., large pelagics, megafauna) are recorded using `transect = "OFF"`. These are included for completeness but should be treated separately in standardized analyses.:::```{r}# Define table schemablt_obs_schema <- blt_obs_fields |>mutate(mode =if_else(required, "REQUIRED", "NULLABLE")) |>transmute(name = field,type = type,mode = mode,description = description) |>pmap(function(name, type, mode, description) {list(name = name, type = type, mode = mode, description = description) })bq_table_create(bq_table(project_id, "uvs", "blt_observations"),fields = blt_obs_schema)```##### `uvs.blt_biomass_by_taxa`This table contains station-level summaries of reef fish biomass and abundance by species, derived from raw BLT transect observations. It is the primary analysis-ready table for generating summaries, figures, and community structure plots.Each row represents a unique combination of `ps_station_id` and species, with metrics standardized to g/m² (biomass) and ind./m² (abundance). All values are computed using species-specific length–weight parameters from the `taxonomy.fish` table.Taxonomic and ecological fields — including family, trophic group, and functional group — are joined from the central species reference and enable flexible aggregation for trophic or functional analyses.```{r eval = T, include = T}#| label: tbl-blt-biomass-schema#| tbl-cap: "`uva.blt_biomass_by_taxa` Table Schema"#| blt_biomass_by_taxa_fields <- tribble( ~field, ~type, ~required, ~description, "ps_station_id", "STRING", TRUE, "◂ Foreign key to `uvs.blt_stations`", "exp_id", "STRING", TRUE, "◂ Expedition ID (`ISO3_YEAR`)", "ps_site_id", "STRING", TRUE, "◂ Site ID for spatial grouping", "region", "STRING", TRUE, "◂ Region name", "subregion", "STRING", TRUE, "◂ Subregion name", "depth_strata", "STRING", TRUE, "Binned depth category: `supershallow`, `shallow`, or `deep`", "depth_m", "FLOAT", TRUE, "Average depth of the station (m)", "aphia_id", "STRING", TRUE, "◂ Species ID (linked to `taxonomy.fish`)", "accepted_name", "STRING", FALSE, "Scientific name (`Genus species`)", "family", "STRING", FALSE, "Taxonomic family (e.g., Acanthuridae)", "trophic_group", "STRING", FALSE, "Ecological trophic group (e.g., piscivore, herbivore)", "functional_group", "STRING", FALSE, "Functional role (e.g., browser, planktivore)", "count", "INTEGER", FALSE, "Total number of individuals observed", "abundance_m2", "FLOAT", FALSE, "Average density (individuals per m²)", "biomass_gm2", "FLOAT", FALSE, "Average estimated biomass (g/m²)")gt(blt_biomass_by_taxa_fields) |> cols_label(field = md("**Field**"), type = md("**Type**"), required = md("**Required**"), description = md("**Description**")) |> cols_width(field ~ px(200), type ~ px(100), required ~ px(80), description ~ px(500)) |> data_color(columns = c(field), fn = scales::col_factor(palette = c("#f6f6f6"), domain = NULL) ) |> tab_options(table.font.size = px(13), table.width = pct(100)) |> fmt_tf(columns = required, tf_style = "true-false") |> fmt_markdown(columns = description) |> gt_theme_nytimes()``````{r}# Define table schemablt_biomass_schema <- blt_biomass_by_taxa_fields |>mutate(mode =if_else(required, "REQUIRED", "NULLABLE")) |>transmute(name = field,type = type,mode = mode,description = description) |>pmap(function(name, type, mode, description) {list(name = name, type = type, mode = mode, description = description) })bq_table_create(bq_table(project_id, "uvs", "blt_biomass_by_taxa"),fields = blt_biomass_schema)```